package com.ld.common.filter;

import java.io.IOException;
import java.util.Base64;
import java.util.Base64.Decoder;
import java.util.List;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.util.AntPathMatcher;

import com.alibaba.druid.util.PatternMatcher;
import com.alibaba.druid.util.ServletPathMatcher;
import com.ld.shieldsb.common.core.collections.ListUtils;
import com.ld.shieldsb.common.core.model.PropertiesModel;
import com.ld.shieldsb.common.core.util.IPUtil;
import com.ld.shieldsb.common.core.util.SimplePathMatcher;
import com.ld.shieldsb.common.core.util.StringUtils;
import com.ld.shieldsb.common.web.util.WebUtil;

import lombok.extern.slf4j.Slf4j;

/**
 * 
 * 安全验证过滤器（用于ip拦截basic验证等）
 * 
 * @ClassName AntiSqlInjectionfilter
 * @author <a href="mailto:donggongai@126.com" target="_blank">吕凯</a>
 * @date 2018年11月15日 上午11:39:15
 *
 */
@Slf4j
public class SecurityFilter implements Filter {
    protected List<String> urlExclusion = null; // 排除不过滤的的url
    protected AntPathMatcher matcher = new AntPathMatcher();
    protected PatternMatcher pathMatcher = new ServletPathMatcher();
    protected SimplePathMatcher matcherSimple = new SimplePathMatcher();

    public void destroy() {
    }

    public void init(FilterConfig arg0) throws ServletException {
    }

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {

        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse resp = (HttpServletResponse) response;
        String servletPath = req.getServletPath();

        if (urlExclusion != null && isExclusion(servletPath)) {
            chain.doFilter(request, response);
        } else {
            String ip = WebUtil.getIpAddr(req);

            if (!checkIP(req, resp)) {
                log.debug("拦截============IP：" + ip + " servletPath:" + servletPath);
//                Web.Response.error(response, "没有访问权限！");
                req.setAttribute("path", servletPath);
                req.setAttribute("message", "没有访问权限");
                req.setAttribute("status", 401);
                req.setAttribute("error", "IP：" + ip);
                req.getRequestDispatcher("/WEB-INF/view/error/error.jsp").forward(request, response);
            } else {
                if (!checkHeaderAuth(req, resp)) {
                    String authName = PropertiesModel.CONFIG.getString("security.access.authname");
                    resp.setStatus(401);
                    resp.setHeader("Cache-Control", "no-store");
                    resp.setDateHeader("Expires", 0);
                    resp.setHeader("WWW-authenticate", "Basic Realm=\"" + authName + "\""); // 用户名

                    req.setAttribute("path", servletPath);
                    req.setAttribute("message", "验证失败");
                    req.setAttribute("status", 401);
                    req.setAttribute("error", "IP：" + ip);
                    req.getRequestDispatcher("/WEB-INF/view/error/error.jsp").forward(request, response);
                    return;
                }
                chain.doFilter(request, response);
            }
        }

    }

    /**
     * 校验
     * 
     * @Title checkHeaderAuth
     * @author 吕凯
     * @date 2019年1月14日 下午6:25:27
     * @param request
     * @param response
     * @return
     * @throws IOException
     *             boolean
     */
    private boolean checkIP(HttpServletRequest request, HttpServletResponse response) throws IOException {
        String ip = WebUtil.getIpAddr(request);
        List<String> swaggerAccessIp = PropertiesModel.CONFIG.getStringList("security.access.ip"); // TODO需要考虑缓存
        boolean validate = true;
        if (ListUtils.isNotEmpty(swaggerAccessIp)) {
            validate = false;
            for (String accessIp : swaggerAccessIp) {
                if (IPUtil.isInRange(ip, accessIp)) {
                    validate = true;
                    break;
                }
            }
        }
        return validate;
    }

    /**
     * 校验basic auth
     * 
     * @Title checkHeaderAuth
     * @author 吕凯
     * @date 2019年1月14日 下午6:32:10
     * @param request
     * @param response
     * @return
     * @throws IOException
     *             boolean
     */
    private boolean checkHeaderAuth(HttpServletRequest request, HttpServletResponse response) throws IOException {
        String auth = request.getHeader("Authorization"); // 示例：Basic a2V2aW46MTIzNDU2
        List<String> authorization = PropertiesModel.CONFIG.getStringList("security.access.authorization");
        boolean validate = true;
        if (ListUtils.isNotEmpty(authorization)) {
            validate = false;
            if ((auth != null) && (auth.length() > 6)) {
                auth = auth.substring(6, auth.length()); //

                String decodedAuth = getFromBASE64(auth); // base64解码，该字符串经过了加密，加密原文为 用户名:密码
                log.warn("auth decoded from base64 is " + decodedAuth);
                decodedAuth = StringUtils.substringAfter(decodedAuth, ":");
                if (authorization.contains(decodedAuth)) {
                    validate = true;
                }

            }
        }
        return validate;
    }

    private String getFromBASE64(String s) {
        if (s == null)
            return null;
        Decoder encoder = Base64.getDecoder();
        try {
            byte[] b = encoder.decode(s);
            return new String(b);
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 判断是排除的
     * 
     * @Title isExclusion
     * @author 吕凯
     * @date 2019年1月14日 下午5:08:21
     * @param requestURI
     * @return boolean
     */
    public boolean isExclusion(String requestURI) {
        if (urlExclusion == null || requestURI == null) {
            return false;
        }

        for (String pattern : urlExclusion) {
            if (matcherSimple.matches(pattern, requestURI)) {
                log.warn("sql过滤器排除：" + requestURI);
                return true;
            }
        }

        return false;
    }

    public List<String> getUrlExclusion() {
        return urlExclusion;
    }

    public void setUrlExclusion(List<String> urlExclusion) {
        this.urlExclusion = urlExclusion;
    }

}
